let req = import.meta.globEager("./blocks/*.ts")

import { AppModel } from '../../../model/AppModel'
import { AllGPIOUsages } from '../../../model/Stud'
import { Usage } from '../../../model/StudUsage';
import { PartBase } from '../../../model/parts/PartBase'
// import Widget from '../../../../beui/asm.js/ts/widgets/Widget';
import CoderJavaScript from './coder/javascript'

export const blocklib: {[key:string]:any} = {}

Blockly.coder = {
    JavaScript: CoderJavaScript()
}

function Package(blocklib: any) {

    return function (runtime: any) {
        this.runtime = runtime

        /**
         * @returns {object} metadata for this extension and its blocks.
         */
        this.getInfo = function () {}
        
        let funcs: any = {}
        let hats: any = {}
        for(let name in blocklib) {
            if(blocklib[name].run) {
                funcs[name] = function(args: any[], util: any) {
                    if(blocklib[name].run){
                        return  blocklib[name].run(args, util, this.runtime, AppModel.active)
                    }
                }
            }
            if(blocklib[name].hat) {
                hats[name] = {
                    restartExistingThreads: false
                }
            }
        }
        this.getPrimitives = function () {
            return funcs
        }

        this.getHats = function() {
            return hats
        }
    }
}

export default function InitBlocks(scratch3: any) {

    for(let path in req) {

        let blockname = path.replace(/^\.[\\\/]blocks[\\\/]/,'').replace(/\.ts$/i,'')
        // console.log(blockname,path)
        let def = req[path].default
        blocklib[blockname] = def
    
        if(def.init) {
            Blockly.Blocks[blockname] = (function (meta: any) {
                return {
                    init() {
                        // @refact
                        meta.init.apply(this, [AppModel.active, scratch3])
    
                        meta.category && this.setCategory(meta.category)
                        meta.color && this.setColour(... meta.color)
                        meta.previousStatement!=undefined && this.setPreviousStatement(meta.previousStatement)
                        meta.nextStatement!=undefined && this.setNextStatement(meta.nextStatement);
                        meta.inputsInline!=undefined && this.setInputsInline(!!meta.inputsInline);

                        if(meta.extensions) {
                            for(let ext of meta.extensions) {
                                Blockly.Extensions.apply(ext, this)
                            }
                        }

                        if(meta.output == 'boolean') {
                            this.setInputsInline(true);
                            this.setOutputShape(Blockly.OUTPUT_SHAPE_HEXAGONAL);
                            this.setOutput(true, 'Boolean');
                        }

                        if(meta.eventHandle) {
                            this.isEventHandleFunction = true
                        }
                    }
                }
            })(def)
        }

        // 注册 coder
        if(def.coder) {
            for(let lang in def.coder) {
                if(!Blockly.coder[lang]) {
                    Blockly.coder[lang] = new Blockly.Generator(lang)
                }
                (function(lang: string){

                    Blockly.coder[lang][blockname] = function(block: any) {
                        return def.coder[lang].call(Blockly.coder[lang], block, Blockly.coder[lang], AppModel.active)
                    }

                }) (lang)
            }
        }
    }

    
    // 扩展积木包
    VirtualMachine.Runtime.defaultBlockPackages["beparts"] = Package(blocklib)
}


/*

  this.setInputsInline(true);
  this.setPreviousStatement(true, null);
  this.setNextStatement(true, null);
*/


export function createVarMenu(scratch3: any) {
    return new Blockly.FieldDropdown(() => {
        if(!scratch3.workspace)
            return []
        let menu = []
        for(let varblock of scratch3.workspace.variableMap_.getAllVariables()){
            menu.push([varblock.name, varblock.name])
        }
        return menu
    })
}

export function createGPIOMenu(model: AppModel, needWritable: boolean=false) {
    return new Blockly.FieldDropdown(async () => {
        let menu = []
        if(model && model.masterPart) {
            for(let row of model.masterPart.studs){
                for(let stud of row){
                    if(AllGPIOUsages.canbeGPIO(stud.optUsages)) {
                        if(needWritable && !stud.optUsages.includes(Usage.gpiooutput))
                            continue
                        menu.push([ stud.info.title||stud.info.name, stud.info.name ])
                    }
                }
            }
        }
        return menu
    })
}


export function createPartMenu(model: AppModel, clazz: new (...arg:any[]) => PartBase) {
    return new Blockly.FieldDropdown(() => {
        if(model) {
            return model.reducePart((arr, part)=>{
                arr.push([part.instanceName, part.uuid])
                return arr
            }, clazz, [])
        }
        else 
            return []
    })
}


function checkMembers(object: any, hasMembers: string[]) {
    if(hasMembers && hasMembers.length) {
        for(let memName of hasMembers) {
            if( !(memName in object) ) {
                return false
            }
        }
    }
    return true
}
export function createUIWidgetMenu(model: AppModel, className: string|null=null, hasMembers: string[]=[]){
    return new Blockly.FieldDropdown(() => {
        if(!model) 
            return []

        let arr: any[] = []

        // @refact
        // model.ui.each((widget: Widget, wid:string)=>{
        //     if(className && className!=widget.constructor.name)
        //         return
        //     if(!checkMembers(widget,hasMembers))
        //         return

        //     arr.push([ widget.name, wid ])   
        // })

        return arr
    })
}